//
//  DPObjCRuntime.h
//  HigherOrderMessaging
//
//  Created by Ofri Wolfus on 03/11/06.
//  Copyright 2006 Ofri Wolfus. All rights reserved.
//

#ifndef _DP_OBJCRUNTIME_H
#define _DP_OBJCRUNTIME_H

#import <HigherOrderMessaging/DPMacros.h>
#import <HigherOrderMessaging/DPObjC-Compatibility.h>

//
//  Size and alignment of types
//
#if __OBJC2__
#define sizeOfType(t...) ({ NSUInteger __s; NSGetSizeAndAlignment(t, &__s, NULL); __s; })
#define alignOfType(t...) ({ NSUInteger __s; NSGetSizeAndAlignment(t, NULL, &__s); __s; })
#else
#define sizeOfType(t...) ({ unsigned int __s; NSGetSizeAndAlignment(t, &__s, NULL); __s; })
#define alignOfType(t...) ({ unsigned int __s; NSGetSizeAndAlignment(t, NULL, &__s); __s; })
#endif


//
//  Type descriptions
//
DP_EXTERN unsigned dp_getNumberOfArguments(const char *typedesc);
DP_EXTERN unsigned dp_getArgumentInfo(const char *typedesc, int argIndex, const  char** type, int*  offset);
DP_EXTERN unsigned dp_getSizeOfArguments(const char *typedesc);


//
//  Classes and objects inspection
//
#define class_isClass(cls) class_isMetaClass(cls->isa)
DP_EXTERN BOOL class_isSubclassOfClass(Class cls, Class superCls);

#define object_isInstance(obj)					class_isClass(obj->isa)
#define object_getClass(obj)					(obj->isa)
#define object_isKindOfClass(obj, superCls)		class_isSubclassOfClass(object_getClass(obj), superCls)

DP_EXTERN_INLINE BOOL object_respondsToSelector(id object, SEL sel);	// Handles both objects and classes

DP_EXTERN_INLINE Method dp_getMethod(id obj, SEL sel);

DP_STATIC_INLINE Class dp_getSuper(Class cls, const char *name) {
	while ((cls = class_getSuperclass(cls)))
		if (strcmp(class_getName(cls), name) == 0)
			return cls;
	
	return Nil;
}


#define Super(name, X...) \
method_getImplementation(class_getInstanceMethod(dp_getSuper(object_isInstance(self) ? \
															 object_getClass(self) : self, #name),\
												 _cmd), _cmd)(self, _cmd, X)


//
//  Selectors
//
DP_EXTERN_INLINE unsigned int sel_getNumberOfArguments(SEL sel);	// Always takes the self and _cmd arguments in count


//
//  Method description
//

/*
 * If you need the method's implementation, you should use MethodDescription
 * instead of Method/objc_method and objc_method_description.
 */
struct dp_method_description {	// Identical to objc_method_description
	SEL name;
	char *types;
};
typedef struct dp_method_description *MethodDescription;

DP_EXTERN_INLINE SEL description_getName(MethodDescription desc);
DP_EXTERN_INLINE const char *description_getTypes(MethodDescription desc);
DP_EXTERN_INLINE void description_setName(MethodDescription desc, SEL name);
DP_EXTERN_INLINE void description_setTypes(MethodDescription desc, const char *types /* Not copied */);

// Returns a MethodDescription from a given method
#define dp_getMethodDescription(m) \
({ \
	struct dp_method_description __desc; \
	description_setName(&__desc, method_getName(m)); \
	description_setTypes(&__desc, method_getTypeEncoding(m)); \
	&__desc; \
})

// Copies a MethodDescription from a given method
DP_EXTERN_INLINE MethodDescription dp_copyMethodDescription(Method m);
//DP_EXTERN_INLINE void description_free(MethodDescription d);

DP_EXTERN_INLINE unsigned description_getNumberOfArguments(MethodDescription d);
DP_EXTERN_INLINE unsigned description_getArgumentInfo(MethodDescription d, int argIndex,
													  const  char** type, int*  offset);
DP_EXTERN_INLINE unsigned description_getSizeOfArguments(MethodDescription d);
DP_EXTERN_INLINE MethodDescription description_createDescription(SEL name, const char *types);


//
//  Allocation/copying of marg_list
//

#define dp_marg_malloc(argsSize) malloc(dp_marg_prearg_size + (7 + argsSize & ~7) /* <- huh? */)

#define dp_marg_alloc(method) dp_marg_malloc(method_getSizeOfArguments(method))
#define dp_marg_copy(margs, method) \
	({ marg_list __marg = dp_marg_alloc(method); memcpy(__marg, margs, malloc_size(__marg)); })


//
//  Getting arguments
//

//DP_EXTERN_INLINE void dp_getArgumentAtIndex(dp_marg_list args, unsigned index, void *buffer);


//
//  Messaging
//

DP_EXTERN id dp_msgSendv(id target, SEL sel, unsigned args_size, marg_list args);


#endif //_DP_OBJCRUNTIME_H

